Skip to content

Conversation

RobinMalfait
Copy link
Member

@RobinMalfait RobinMalfait commented Oct 6, 2025

This PR improves the performance of when we need to clone some AST nodes. We have a few places where we clone Candidate, Variant and CSS AST nodes.

Right now we use structuredClone, which works, but it is a generic solution. However, we do know the exact structure of these AST nodes, so we can write specialized clone functions that are much faster.

Test plan

  1. All the tests still pass with this change
  2. The performance is better:
  cloneCandidate - src/candidate.bench.ts > Candidate cloning
    1.72x faster than cloneCandidate (spread)
    74.03x faster than structuredClone

  cloneAstNode() - src/ast.bench.ts > Cloning AST nodes
    1.15x faster than cloneAstNode (with spread)
    33.54x faster than structuredClone()

Ready for review, but should be merged after #19059

@RobinMalfait RobinMalfait force-pushed the feat/canonicalize-candidates branch 2 times, most recently from ccbd48d to 226ae47 Compare October 6, 2025 15:49
@RobinMalfait RobinMalfait force-pushed the feat/improve-cloning-performance branch from 3b92b93 to 2024aa8 Compare October 6, 2025 15:49
@RobinMalfait RobinMalfait marked this pull request as ready for review October 6, 2025 17:03
@RobinMalfait RobinMalfait requested a review from a team as a code owner October 6, 2025 17:03
Base automatically changed from feat/canonicalize-candidates to main October 7, 2025 10:41
Our AST nodes are simple plain objects with a `kind` as the discriminant
instead of of an actual class instance. Since we're dealing with simple
plain objects, there is no need for a heavy handed `structuredClone` and
we can use this function instead.

Also added some benchmarks which you can run to see how much the
difference is. On my M1 Pro Max while using battery, these are the
results:

```
  ✓ Cloning AST nodes (2) 2462ms
     name                         hz     min     max    mean     p75     p99    p995    p999     rme  samples
   · cloneAstNode()     5,769,838.55  0.0000  0.0467  0.0002  0.0002  0.0004  0.0004  0.0005  ±0.09%  2884920   fastest
   · structuredClone()    222,598.38  0.0042  4.5958  0.0045  0.0045  0.0048  0.0049  0.0100  ±1.81%   111300

 BENCH  Summary

  cloneAstNode() - ast.bench.ts > Cloning AST nodes
    25.92x faster than structuredClone()
```
These are much faster compared to `structuredClone`. This is again
because we're dealing with simple plain objects.
Turns out that if you keep the exact same structure instead of a spread,
it's even faster!

```
  cloneAstNode() - src/ast.bench.ts > Cloning AST nodes
    1.15x faster than cloneAstNode (with spread)
    33.54x faster than structuredClone()
```
```
  cloneCandidate - src/candidate.bench.ts > Candidate cloning
    1.72x faster than cloneCandidate (spread)
    74.03x faster than structuredClone
```
Strings are immutable, so the only thing we're cloning here is the array
itself.
This just means that if you pass in an static candidate, that a static
candidate comes out. Otherwise an static candidate results in the
generic `Candidate` type which is too wide.
@RobinMalfait RobinMalfait force-pushed the feat/improve-cloning-performance branch from 2024aa8 to 7e26cfc Compare October 7, 2025 10:42
@RobinMalfait RobinMalfait enabled auto-merge (squash) October 7, 2025 10:43
@RobinMalfait RobinMalfait merged commit efe084b into main Oct 7, 2025
7 checks passed
@RobinMalfait RobinMalfait deleted the feat/improve-cloning-performance branch October 7, 2025 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants